<?php

class XQL_Object
{
	/**
	 * @var Object
	 */
	public $creator;

	/**
	 * Az osztályhoz tartozó objektum séma, amely meghatározza, hogyan épül fel az adott
	 * objektum. A kiinduló séma ennek az objektumnak a sémája.
	 *
	 * @var XQL_ObjectSchema
	 */
	protected $objectSchema;
	
	/**
	 * Az objektum neve. Meg kell egyeznie egy sémának a nevével, amelyre illeszthető.
	 *
	 * @var string
	 */
	protected $objectName = 'default';
	
	/**
	 * Az aktuális XQL_Connection.
	 *
	 * @var XQL_Connection
	 */
	protected $connection;
	
	/**
	 * A csatlakoztatott elemek listája
	 *
	 * @var array XQL_Object
	 */
	protected $joinedObjects = array();
	
	/**
	 * Az objektum értéke.
	 *
	 * @var array
	 */
	protected $objectValue = array();
	
	/**
	 * Felépíti az objektum alapértelmezéseit.
	 *
	 * @param PDO $pdo
	 */
	public function __construct()
	{
		$this->connection = x10::dedicate('XQL_Connection_DatabaseManager')->getActive();
		$this->buildSchema();
	}
	
	/**
	 * Felépíti az osztályhoz tartozó sémát. Implementálása kötelező.
	 *
	 */
	protected function buildSchema()
	{
		throw new XQL_E_Object_SchemaRequired(get_class($this));
	}
	
	/**
	 * Visszatér az objektum szerkezeti sémájával.
	 *
	 * @return string
	 */
	public function getSchemaMap()
	{
		return (string)$this->objectSchema;
	}
	
	/**
	 * Visszatér egy példánnyal ugyanebből az osztályból.
	 *
	 * @return XQL_Object
	 */
	public static function create()
	{
		return new XQL_Object();
	}
	
	/**
	 * Beállítja az objektumhoz tartozó XQL_Connection-t.
	 *
	 * @param XQL_Connection $connection
	 * @return XQL_Object Sajátmaga
	 */
	public function setConnection(XQL_Connection $connection)
	{
		$this->connection = $connection;

		if (is_array($this->objectValue) and count($this->objectValue) > 0)
		{
			foreach ($this->objectValue as $object)
			{
				if (is_object($object) and $object instanceof XQL_Object)
				{
					$object->setConnection($this->connection);
				}
			}
		}
		
		if (is_array($this->joinedObjects) and count($this->joinedObjects) > 0)
		{
			foreach ($this->joinedObjects as $object)
			{
				if (is_object($object) and $object instanceof XQL_Object)
				{
					$object->setConnection($this->connection);
				}
			}
		}
		
		return $this;
	}
	
	/**
	 * Objektumot csatol ehhez az objektumhoz. A csatolni kívánt objektumnak
	 * illeszkednie kell a sémára, különben XQL_E_Object_InvalidJoin kivétel keletkezik.
	 *
	 * @param XQL_Object $object
	 * @param string $floatedName
	 * @return XQL_Object Sajátmaga
	 */
	public function join($object, $floatedName='')
	{
		if ($this->objectSchema->validateObject($object))
		{
			$this->joinedObjects[$object->objectName] = $object;
			$this->joinedObjects[$object->objectName]->creator = $this;
		}
		else
		{
			throw new XQL_E_Object_InvalidJoin(get_class($object));
		}
		
		return $this;
	}
	
	/**
	 * Értéket rendel az objektumhoz. Ha a $value egy tömb, akkor a többi
	 * paramétert nem veszi figyelembe. Egyébként tetszőleges számú paraméter
	 * adható meg.
	 *
	 * @param mixed $value [$value, ...]
	 * @return XQL_Object Az aktuális objektum
	 */
	public function set($value)
	{
		if (!is_array($value))
		{
			$value = func_get_args();
		} // if
		$this->objectValue = $value;
		
		return $this;
	}
	
	/**
	 * Értéket ad hozzá az objektum meglévő értékeihez.
	 *
	 * @param mixed $value
	 * @return XQL_Object Az aktuális objektum
	 */
	public function add($value)
	{
		$this->objectValue[] = $value;
		
		return $this;
	}
	
	/**
	 * Kizárólag ennek az objektumnak az értékével tér vissza. Implementációja
	 * kötelező. A további megvalósításokban ügyelni kell arra, hogy az egyes
	 * érték elemek akár olyan objektumok is lehetnek, amelyek string-ként
	 * térhetnek vissza (például más XQL_Object leszármazottak).
	 *
	 * @return string
	 */
	public function get()
	{
		throw new XQL_E_Object_GetRequired(get_class($this));
	}
	
	/**
	 * Visszatér az objektum technikai nevével.
	 *
	 * @return string
	 */
	public function getObjectName()
	{
		return $this->objectName;
	}

	/**
	 * Lehetővé teszi az objektum számára, hogy tulajdonságként legyenek elérhetőek
	 * a már csatlakoztatott alobjektumok.
	 *
	 * @param string $name
	 * @return XQL_Object
	 */
	public function &__get($name)
	{
		if (isset($this->joinedObjects[$name]))
		{
			return $this->joinedObjects[$name];
		}
		else
		{
			$NULL = NULL;
			return $NULL;
		}
	}

	/**
	 * Lehetővé teszi az objektum számára, hogy a tulajdonságként csatlakoztatott
	 * alobjektumok megléte ellenőrizhető legyen.
	 *
	 * @param string $name
	 * @return XQL_Object
	 */
	public function __isset($name)
	{
		return isset($this->joinedObjects[$name]);
	}
	
	/**
	 * Lecsatlakoztatja az objektumot a szülőobjektumról.
	 *
	 * @param $returnDetached Ha TRUE, a visszatérés a lekapcsolt objektum lesz.
	 * @return XQL_Object A szülőobjektum
	 */
	public function detach($returnDetached=FALSE)
	{
		$parent = $this->creator;
		$result = $parent->detachObject($this->objectName, $returnDetached);
		
		return $returnDetached? $result : $parent;
	}
	
	/**
	 * Lecsatlakoztat egy alobjektumot.
	 *
	 * @param string $name
	 */
	public function detachObject($name)
	{
		$this->joinedObjects[$name] = NULL;
		unset($this->joinedObjects[$name]);
		
		return $this->joinedObjects[$name];
	}
	
	/**
	 * A kapott egyedi azonosító segítségével a séma alapján az objektumokból
	 * felépíti az objektum string reprezentációját, a megfelelő helyeken a
	 * megadott objektumértékek szerepeltetésével.
	 *
	 * @param string $id
	 * @return string
	 */
	protected function build($id)
	{
		$this->objectSchema->setId($id);
		$format = (string)$this->objectSchema;
		$formatId = '{'.$this->objectName.'#'.$id.'}';
		$value = $this->get();
		$result = '';
		$content = '';
		
		if ($value == '')
		{
			// Az összeépítés a sémában meghatározott sorrendben kell történjen.
			foreach ($this->objectSchema->getSubSchemasName() as $schemaName)
			{
				if (isset($this->joinedObjects[$schemaName]))
				{
					$content .= ' '.$this->joinedObjects[$schemaName]->build($id);
				}
			}
		}
		else
		{
			$content = $value;
		}
		
		if (!empty($content))
		{
			$result = str_replace($formatId, $content, $format);
		}
		
		return $result;
	}
	
	/**
	 * Megvalósítja az objektum string reprezentációját.
	 *
	 * @return string
	 */
	public function __toString()
	{
		$id = uniqid();
		return $this->build($id);
	}
}

?>